﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;

namespace CC.Common
{
    public class MyConfiguration
    {
        private class Variable
        {
            public string Value;
            public bool Changed;

            public Variable(string value)
            {
                Value = value;
                Changed = false;
            }
        }
        private string mFilePath;
        private Dictionary<string, Variable> mDictionaries;
        private bool encrypt;
        public MyConfiguration( bool encrypt=true)
        {
            this.encrypt = encrypt;
            mDictionaries = new Dictionary<string, Variable>();
        }
        public MyConfiguration(string confFile,bool encrypt = true)
        {
            this.encrypt = encrypt;
            mDictionaries = new Dictionary<string, Variable>();
            Load(confFile);
        }
        /// <summary>
        /// 从文件读取配置信息到类实例
        /// 要求文件采用Myconfiguration类保存，且key为类名.属性名
        /// </summary>
        /// <typeparam name="T">包含配置信息的泛型类</typeparam>
        /// <param name="obj">用于返回的类实例</param>
        /// <param name="configFile">指向的配置文件</param>
        public void ReadConfigurationsToObject<T>(T obj, string configFile="")
        {
            if (obj == null) return;
            if(!string.IsNullOrEmpty(configFile))
                Load(configFile);            

            Type type = obj.GetType();
            var propertyInfos = type.GetProperties();
            //
            foreach (var f in propertyInfos)
            {
                string key = $"{type.Name}.{f.Name}";
                if (ContainsKey(key))
                {
                    string typeName = f.PropertyType.Name;
                     if (f.PropertyType.IsEnum)
                    {
                        var val = Enum.Parse(f.PropertyType, ReadString(key));
                        f.SetValue(obj, val, null);
                    }
                    else if (typeName == "String")
                    {
                        string text = ReadString(key);
                        f.SetValue(obj, text, null);
                    }
                    else if (typeName == "Double")
                    {
                        f.SetValue(obj, ReadDouble(key), null);
                    }
                    else if (typeName == "Single")
                    {
                        f.SetValue(obj, (Single)ReadDouble(key), null);
                    }
                    else if (typeName == "Float")
                    {
                        f.SetValue(obj, ReadFloat(key), null);
                    }
                    else if (typeName == "Int32" || typeName == "Int16" || typeName == "Int64")
                    {
                        f.SetValue(obj, ReadIntger(key), null);
                    }
                    else if (typeName == "Boolean")
                    {
                        f.SetValue(obj, ReadBoolean(key), null);
                    }
                    else if (typeName == "DateTime")
                    {
                        string str = ReadString(key);
                        if(!string.IsNullOrEmpty(str))
                        {
                            if(DateTime.TryParse(str,out DateTime time))
                            {
                                f.SetValue(obj, time, null);
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 自动保存类实例到配置文件
        /// 产生的保存项格式为:Classname.FieldName=Value
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj">带有配置信息的类实例</param>
        /// <param name="configFile">配置文件,如果没有输入，则保存到实例默认的配置文件名</param>
        public void WriteConfigurationsFromObject<T>(T obj,string configFile="")
        {
            string file = string.IsNullOrEmpty(configFile) ? mFilePath : configFile;
                
            Type type = obj.GetType();
            var propertyInfos = type.GetProperties();
            //保存成Classname.FieldName=Value的形式
            foreach (var f in propertyInfos)
            {
                string key = $"{type.Name}.{f.Name}";
                WriteString(key, f.GetValue(obj, null).ToString());
            }
            Save(file);
        }


        public int TotalKeys()
        {
            return mDictionaries.Count;
        }
        public void Load(string filePath)
        {            
            mDictionaries.Clear();
            mFilePath = filePath;
            StreamReader reader = new StreamReader(mFilePath);
            string line, key, value;
            int index;
            while (!reader.EndOfStream)
            {
                line = reader.ReadLine();
                if(encrypt)line = Decrypt(line);
                index = line.IndexOf('!');//忽略注释
                if (index>=0) line = line.Substring(0,index);
                index = line.IndexOf('！');//忽略注释，兼容中文注释符
                if (index >= 0) line = line.Substring(0, index);
                index = line.IndexOf('=');
                if (index <0)
                    continue;
                key = line.Substring(0, index);
                if (string.IsNullOrEmpty(key))
                    continue;

                if (index >= line.Length - 1)
                    value = string.Empty;
                else
                    value = line.Substring(index + 1);

                mDictionaries.Add(key, new Variable(value));
            }
            reader.Close();
        }
        
        public void Save()
        {
            Save(mFilePath);
        }

        public void Save(string filePath)
        {
            StreamWriter writer = new StreamWriter(filePath, false);

            ArrayList keyList = new ArrayList(mDictionaries.Keys);
            keyList.Sort();
            if (encrypt)
            {
                foreach (string key in keyList)
                    writer.WriteLine(Encrypt(key + "=" + mDictionaries[key].Value));
            }
            else
            {
                foreach (string key in keyList)
                    writer.WriteLine(key + "=" + mDictionaries[key].Value);
            }
               
            writer.Close();
        }

        public void Clear()
        {
            mDictionaries.Clear();            
        }
        /// <summary>
        /// Same to ContainsKey();
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Exist(string key)
        {
            return mDictionaries.ContainsKey(key);
        }

        public bool ContainsKey(string key)
        {
            return mDictionaries.ContainsKey(key);
        }
        
       
        public void WriteString(string key, string value)
        {
            if (mDictionaries.ContainsKey(key))
            {
                if (mDictionaries[key].Value != value)
                {
                    mDictionaries[key].Value = value;
                    mDictionaries[key].Changed = true;
                }
                return;              
            }
            else
            {
                mDictionaries.Add(key, new Variable(value));
                mDictionaries[key].Changed = true;
                //throw (new Exception("不存在变量 '" + key + "'，写入失败."));
            }
        }
        /// <summary>
        /// 因为文件是以文本方式保存，所以本方法为读出配置的基本方法
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string ReadString(string key)
        {
            string value = string.Empty;
            if (mDictionaries.ContainsKey(key))
                value= mDictionaries[key].Value;
            return value;          
        }

        public void WriteIntger(string key, int value)
        {
            WriteString(key, value.ToString());
        }

        public int ReadIntger(string key)
        {
            int result = 0;
           
            string value = ReadString(key);
            if (!int.TryParse(value, out result))
            {
                result = 0;
            }
            return result;
        }

        public void WriteDouble(string key, double value)
        {
            WriteString(key, value.ToString());
        }
       
        public double ReadDouble(string key)
        {
            double result = 0;
            string value = ReadString(key);
            if (!double.TryParse(value, out result))
            {
                result = 0;
            }
            return result;
        }
        public void WriteFloat(string key, float value)
        {
            WriteString(key, value.ToString());
        }
        public float ReadFloat(string key)
        {
            string value = ReadString(key);
            if (float.TryParse(value, out float result))
                return result;
            else
                return 0;
        }
        public void WriteBoolean(string key, bool value)
        {
            WriteString(key, value.ToString());
        }

        public bool ReadBoolean(string key)
        {
            
           
            string value = ReadString(key);
            if (!bool.TryParse(value, out bool result))
            {
                result = false;               
            }
            return result;
        }

        public bool IsValueChanged()
        {
            foreach (KeyValuePair<string, Variable> entry in mDictionaries)
            {
                if (entry.Value.Changed)
                    return true;
            }
            return false;
        }

        public bool IsValueChangedForSection(string section)
        {
            foreach (KeyValuePair<string, Variable> entry in mDictionaries)
            {
                if (entry.Key.Contains(section + "."))
                {
                    if (entry.Value.Changed)
                        return true;
                }
            }
            return false;
        }

        public bool IsValueChangedForKey(string key)
        {
            
            foreach (KeyValuePair<string, Variable> entry in mDictionaries)
            {
                if (entry.Key == key)
                {
                    if (entry.Value.Changed)
                        return true;
                }
            }
            return false;
        }

        public MyConfiguration GetChanges(MyConfiguration config)
        {
            MyConfiguration result = new MyConfiguration();
            result.mDictionaries.Clear();
            foreach (KeyValuePair<string, Variable> entry in mDictionaries)
                result.mDictionaries.Add(entry.Key, new Variable(entry.Value.Value));
            foreach(KeyValuePair<string, Variable> entry in config.mDictionaries)
            {
                if (result.ContainsKey(entry.Key))
                    result.WriteString(entry.Key, entry.Value.Value);
            }
            return result;
        }

       

        private byte[] RgbKey = { 0x64, 0x6C, 0x41, 0x4B, 0x63, 0x56, 0x49, 0x67 };
        private byte[] RgbIV = { 0x18, 0x52, 0x36, 0x98, 0x90, 0xAE, 0xBE, 0xDF };

        private string Encrypt(string encryptString)
        {
            try
            {
                byte[] rgbKey = RgbKey;
                byte[] rgbIV = RgbIV;
                byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString);
                DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider();
                MemoryStream mStream = new MemoryStream();
                CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
                cStream.Write(inputByteArray, 0, inputByteArray.Length);
                cStream.FlushFinalBlock();
                return Convert.ToBase64String(mStream.ToArray());
            }
            catch
            {
                return encryptString;
            }
        }

        private string Decrypt(string decryptString)
        {
            try
            {
                byte[] rgbKey = RgbKey;
                byte[] rgbIV = RgbIV;
                byte[] inputByteArray = Convert.FromBase64String(decryptString);
                DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider();
                MemoryStream mStream = new MemoryStream();
                CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
                cStream.Write(inputByteArray, 0, inputByteArray.Length);
                cStream.FlushFinalBlock();
                return Encoding.UTF8.GetString(mStream.ToArray());
            }
            catch
            {
                return decryptString;
            }
        }
    }
}
